import subprocess
import os
import sys
from datetime import datetime
from logger import Logger


class Repo:
    """Git repository operation class"""

    def __init__(self, repo_uri, local_path, backup_dir):
        """
        Initialize repository

        Parameters:
            repo_uri (str): Remote repository URI
            local_path (str): Local repository path
        """
        self.repo_uri = repo_uri
        self.local_path = local_path
        self.backup_dir = backup_dir

    def _check_updates(self, remote_name="origin", branch="HEAD"):
        """
        Check if Git remote repository has updates (without pulling code)

        Parameters:
            remote_name (str): Remote repository name (default origin)
            branch (str): Branch to compare (default HEAD)

        Returns:
            bool: True if updates available, False if no updates
        """
        try:
            if not os.path.exists(self.local_path):
                return True
            # Get remote repository latest commit ID
            remote_cmd = f"git -C {self.local_path} ls-remote {remote_name} {branch}"
            remote_hash = (
                subprocess.check_output(remote_cmd.split()).decode().split()[0]
            )

            # Get local current branch latest commit ID
            local_cmd = f"git -C {self.local_path} rev-parse {branch}"
            local_hash = subprocess.check_output(local_cmd.split()).decode().strip()

            # Compare commit IDs
            if remote_hash == local_hash:
                Logger.info("Local code is up to date, no updates")
                return False
            else:
                Logger.warning("Remote updates available!")
                print(f"Local commit: {local_hash}")
                print(f"Remote commit: {remote_hash}")
                print("Tip: Run `git pull` to get updates")
                return True

        except subprocess.CalledProcessError as e:
            Logger.error(f"Error executing Git command: {e}")
            sys.exit(1)

    def _backup_local_changes(self):
        """
        Check if git directory has modifications, if so, backup modifications to backup directory.
        Backup files are timestamped to mark backup time, including untracked files.

        Parameters:
            backup_dir (str): Backup directory path
        """
        try:
            # Check for modifications (including tracked and untracked files)
            result = subprocess.run(
                ["git", "status", "--porcelain"],
                cwd=self.local_path,
                capture_output=True,
                text=True,
                check=True,
            )

            # If no modifications, return directly
            if not result.stdout.strip():
                Logger.info("No local modifications, no backup needed")
                return

            # Generate timestamped backup filename
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_file = os.path.join(
                self.backup_dir, f"changes_backup_{timestamp}.patch"
            )

            # Create patch file containing all modifications (including untracked files)
            # First handle modifications to tracked files
            diff_result = subprocess.run(
                ["git", "diff", "HEAD"],
                cwd=self.local_path,
                capture_output=True,
                text=True,
            )

            with open(backup_file, "w", encoding="utf-8") as f:
                # Write modifications to tracked files
                if diff_result.stdout:
                    f.write(diff_result.stdout)
                    f.write("\n")

                # Handle untracked files
                untracked_result = subprocess.run(
                    ["git", "ls-files", "--others", "--exclude-standard"],
                    cwd=self.local_path,
                    capture_output=True,
                    text=True,
                    check=True,
                )

                untracked_files = untracked_result.stdout.strip().split("\n")
                untracked_files = [f for f in untracked_files if f]  # Remove empty lines

                # Add untracked file contents to patch
                for untracked_file in untracked_files:
                    untracked_file_path = os.path.join(self.local_path, untracked_file)
                    if os.path.isfile(untracked_file_path):
                        f.write(f"--- /dev/null\n")
                        f.write(f"+++ b/{untracked_file}\n")
                        f.write(f"@@ -0,0 +1 @@\n")
                        f.write(f"+(new file content follows)\n")
                        f.write(f"\\ No newline at end of file\n")

                        # Add file content
                        with open(
                            untracked_file_path, "r", encoding="utf-8", errors="ignore"
                        ) as uf:
                            content = uf.read()
                            # Simplified processing, should add + prefix line by line in practice
                            content_lines = content.split("\n")
                            for line in content_lines:
                                if line:  # Only process non-empty lines
                                    f.write(f"+{line}\n")
                            if content and not content.endswith("\n"):
                                f.write("\\ No newline at end of file\n")
                        f.write("\n")

            Logger.success(f"Local modifications backed up to: {backup_file}")

        except subprocess.CalledProcessError as e:
            Logger.error(f"Error executing Git command: {e}")
            sys.exit(1)
        except Exception as e:
            Logger.error(f"Error during backup process: {e}")
            sys.exit(1)

    def _clone_or_update(self):
        """Clone repository or update (if already exists)"""
        if os.path.exists(self.local_path):
            Logger.info(f"Repository already exists at {self.local_path}. Updating...")
            try:
                # Backup local modifications
                self._backup_local_changes()
                # Reset any local modifications
                subprocess.run(
                    ["git", "reset", "--hard", "HEAD"], cwd=self.local_path, check=True
                )
                # Pull latest modifications
                subprocess.run(["git", "pull"], cwd=self.local_path, check=True)
            except subprocess.CalledProcessError as e:
                Logger.error(f"Failed to update repository: {e}")
                raise
        else:
            Logger.info(
                f"Cloning repository from {self.repo_uri} to {self.local_path}..."
            )
            try:
                subprocess.run(
                    ["git", "clone", self.repo_uri, self.local_path], check=True
                )
            except subprocess.CalledProcessError as e:
                Logger.error(f"Failed to clone repository: {e}")
                raise

    def update(self):
        if self._check_updates():
            self._clone_or_update()
            return True
        return False